﻿using System.Linq.Expressions;
using System.Reflection;
using BootstrapBlazor.Components;

namespace BPM.Web.Shared.Extensions;

public static class ComponentExtensions
{
    /// <summary>
    /// 获取方法信息
    /// </summary>
    /// <typeparam name="T">BootstrapComponent类型</typeparam>
    /// <param name="name">方法名称</param>
    /// <param name="bindingAttr">方法类型</param>
    /// <returns></returns>
    public static MethodInfo? GetMethod<T>(string name, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Instance)
        where T : BootstrapComponentBase
        => typeof(T)?.GetMethod(name, bindingAttr);

    /// <summary>
    /// 使用Lambda调用对象无参数方法
    /// </summary>
    /// <typeparam name="TInstance">BootstrapComponent对象</typeparam>
    /// <typeparam name="TResult">返回结果</typeparam>
    /// <param name="instance">组件对象</param>
    /// <param name="method">方法</param>
    /// <returns></returns>
    public static TResult? Call<TInstance, TResult>(this TInstance instance, Lazy<MethodInfo?> method)
        where TInstance : BootstrapComponentBase
    {
        if (method.Value == null)
            return default;

        var constant = Expression.Constant(instance);
        var call = Expression.Call(constant, method.Value);
        var func = Expression.Lambda<Func<TResult>>(call).Compile();
        return func();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TInstance"></typeparam>
    /// <typeparam name="TResult"></typeparam>
    /// <param name="instance"></param>
    /// <param name="method"></param>
    /// <param name="args"></param>
    /// <returns></returns>
    public static TResult? Call<TInstance, TResult>(this TInstance instance, Lazy<MethodInfo?> method, params object?[] args)
        where TInstance : BootstrapComponentBase
    {
        if (method.Value == null)
            return default;

        var constant = Expression.Constant(instance);
        var arguments = args.Select(x => Expression.Parameter(x?.GetType() ?? typeof(object)));
        var call = Expression.Call(constant, method.Value, arguments);
        var func = Expression.Lambda<Func<TResult>>(call).Compile();
        return func();
    }
}

